package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.device.dao.repository.StationCacheRepository;
import cn.tedu.charging.device.pojo.po.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository("CacheGEOANDShareHash")
@Slf4j
public class StationCacheGEOANDShareHashRepository implements StationCacheRepository {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void saveStation2Redis(List<StationPO> stationPOList) {

        Map<Integer, Point> geoMap = new HashMap<Integer, Point>();
        Map<String,StationPO> smallMap = new HashMap<String, StationPO>();
        for (StationPO stationPO : stationPOList) {
            // 获取经纬度
            BigDecimal stationLng = stationPO.getStationLng();
            BigDecimal stationLat = stationPO.getStationLat();
            // 转换为Double 存入Point
            Double lng = Double.valueOf(stationLng.toEngineeringString());
            log.debug("经度{}",lng);
            Double lat = Double.valueOf(stationLat.toEngineeringString());
            log.debug("纬度{}",lat);

            Point point = new Point(lng, lat);
            geoMap.put(stationPO.getId(), point);


            // 定义小Key
            //站点id和基本信息的映射
            String smallKey = "station_info_small_key_" + stationPO.getId();
            smallMap.put(smallKey,stationPO);
        }
        // 定义geo 的 operations 绑定key stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_GEO");
        //批量保存 位置信息 point  和 场站信息 stationPO 到Redis-GEO
        // 对于已经存在的 key，不会覆盖，会自动更新对应的数据
        stations.add(geoMap);
        // 定义大Key
        String bigKey = "station_infos_hash";
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.putAll(bigKey,smallMap);

    }

    @Override
    public List<StationPO> search(Double longitude, Double latitude, Double distance) {
        log.debug("开始根据经纬度{},{},和距离{}进行搜索",longitude,latitude,distance);
        // 定义geo 的 operations 绑定key stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_GEO");
        HashOperations<String,String,StationPO> hashOperations = redisTemplate.opsForHash();
        log.debug("获取stations{}",stations);
        // 定义一个圆 即规定的范围 从范围中获取具体的点的坐标，列表数据，从而获取附近的场站信息点的位置
        Circle circle = getCircle(longitude, latitude, distance);
        log.debug("获取circle{},根据参数",circle);
        // 查询 通过circle
        log.debug("通过RedisGEO Circle:{} 查询附近充电站",circle);
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> results = stations.radius(circle);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站:{}",circle,results);
        List<StationPO> stationPOS= new ArrayList<>();
        for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> result : results) {
            // GeoResults 里的具体内容 用 content表示getContent() 获取里面存入的内容
            RedisGeoCommands.GeoLocation<Integer> stationPOGeoLocation = result.getContent();
            // 获取到对应的场站信息 name 是存入的参数中的 member
            Integer stationId = stationPOGeoLocation.getName();
            // 获取到对应的场站信息
            // 定义大Key
            String bigKey = "station_infos_hash";
            // 定义小Key
            String smallKey = "station_info_small_key_" + stationId;
            // 获取小 map 对应的value 通过大小Key
            StationPO stationPO = hashOperations.get(bigKey, smallKey);

            // 将场站信息存入到集合中
            stationPOS.add(stationPO);
            log.debug("获取到场站信息{}",stationPO);
        }

        return stationPOS;
    }

    private Circle getCircle(Double longitude, Double latitude, Double distance) {
        log.debug("开始根据经纬度{},{},和距离{}进行搜索",longitude,latitude,distance);
        Point point = new Point(longitude, latitude);
        Distance geoDistance = new Distance(distance, Metrics.KILOMETERS);
        Circle circle = new Circle(point, geoDistance);
        log.debug("获取圆{}",circle);
        return circle;
    }
}
